package com.gitee.wsl.collections.trie.analyzer

/**
 * Defines the interface to analyze [Trie][org.apache.commons.collections4.Trie] keys on a bit level.
 * [KeyAnalyzer]'s methods return the length of the key in bits, whether or not a bit is set,
 * and bits per element in the key.
 *
 *
 * Additionally, a method determines if a key is a prefix of another
 * key and returns the bit index where one key is different from another
 * key (if the key and found key are equal than the return value is
 * [.EQUAL_BIT_KEY]).
 *
 *
 * @param <K> the type of objects that may be compared by this analyzer
 * @since 4.0
</K> */
abstract class KeyAnalyzer<K> : Comparator<K?>{
    /**
     * Returns the n-th different bit between key and other. This starts the comparison in
     * key at 'offsetInBits' and goes for 'lengthInBits' bits, and compares to the other key starting
     * at 'otherOffsetInBits' and going for 'otherLengthInBits' bits.
     *
     * @param key  the key to use
     * @param offsetInBits  the bit offset in the key
     * @param lengthInBits  the maximum key length in bits to use
     * @param other  the other key to use
     * @param otherOffsetInBits  the bit offset in the other key
     * @param otherLengthInBits  the maximum key length in bits for the other key
     * @return the bit index where the key and other first differ
     */
    abstract fun bitIndex(
        key: K, offsetInBits: Int, lengthInBits: Int,
        other: K?, otherOffsetInBits: Int, otherLengthInBits: Int,
    ): Int

    /**
     * Returns the number of bits per element in the key.
     * This is only useful for variable-length keys, such as Strings.
     *
     * @return the number of bits per element
     */
    abstract fun bitsPerElement(): Int

    public override fun compare(a: K?, b: K?): Int {
        if (a == null) {
            return if (b == null) 0 else -1
        }
        if (b == null) {
            return 1
        }

        return (a as Comparable<K>).compareTo(b)
    }

    /**
     * Returns whether or not a bit is set.
     *
     * @param key  the key to check, may not be null
     * @param bitIndex  the bit index to check
     * @param lengthInBits  the maximum key length in bits to check
     * @return `true` if the bit is set in the given key and
     * `bitIndex` &lt; `lengthInBits`, `false` otherwise.
     */
    abstract fun isBitSet(key: K?, bitIndex: Int, lengthInBits: Int): Boolean

    /**
     * Determines whether or not the given prefix (from offset to length) is a prefix of the given key.
     *
     * @param prefix  the prefix to check
     * @param offsetInBits  the bit offset in the key
     * @param lengthInBits  the maximum key length in bits to use
     * @param key  the key to check
     * @return `true` if this is a valid prefix for the given key
     */
    abstract fun isPrefix(prefix: K, offsetInBits: Int, lengthInBits: Int, key: K): Boolean

    /**
     * Returns the length of the Key in bits.
     *
     * @param key  the key
     * @return the bit length of the key
     */
    abstract fun lengthInBits(key: K?): Int

    companion object {

        /**
         * Returned by [.bitIndex]
         * if key's bits are all 0.
         */
        const val NULL_BIT_KEY: Int = -1

        /**
         * Returned by [.bitIndex] if key and found key are equal.
         * This is a very specific case and shouldn't happen on a regular basis.
         */
        const val EQUAL_BIT_KEY: Int = -2

        /**
         * Used to test a `bitIndex` in [.isOutOfBoundsIndex].
         */
        const val OUT_OF_BOUNDS_BIT_KEY: Int = -3

        /**
         * Returns true if bitIndex is a [KeyAnalyzer.EQUAL_BIT_KEY].
         */
        fun isEqualBitKey(bitIndex: Int): Boolean {
            return bitIndex == EQUAL_BIT_KEY
        }

        /**
         * Returns true if bitIndex is a [KeyAnalyzer.NULL_BIT_KEY].
         */
        fun isNullBitKey(bitIndex: Int): Boolean {
            return bitIndex == NULL_BIT_KEY
        }

        /**
         * Returns true if bitIndex is a [KeyAnalyzer.OUT_OF_BOUNDS_BIT_KEY].
         */
        fun isOutOfBoundsIndex(bitIndex: Int): Boolean {
            return bitIndex == OUT_OF_BOUNDS_BIT_KEY
        }

        /**
         * Returns true if the given bitIndex is valid.
         * Indices are considered valid if they're between 0 and [Integer.MAX_VALUE]
         */
        fun isValidBitIndex(bitIndex: Int): Boolean {
            return bitIndex >= 0
        }
    }
}
